package wechat

import (
	"context"
	"encoding/xml"
	"errors"
	"net/http"
	"product/common/nets"
	"product/common/validator"
)

// *****************************************************************************
// 微信签订代扣约
// *****************************************************************************

type ContractBase struct {
	// 应用ID
	AppId string
	// 商户号是商户在微信申请微信支付成功后分配的账号ID，登录平台为pay.weixin.qq.com
	MchId string
	// 签名
	Sign string
	// 协议模板id，设置路径见
	PlanId string
	// 商户侧的签约协议号，由商户生成，只能是数字、大小写字母的描述
	ContractCode string
}

// ContractRespBase 签约响应参数
type ContractRespBase struct {
	ReturnCode *CDATA `xml:"return_code,omitempty" json:"return_code,omitempty"`
	ReturnMsg  *CDATA `xml:"return_msg,omitempty" json:"return_msg,omitempty"`
	ResultCode *CDATA `xml:"result_code,omitempty" json:"result_code,omitempty"`
	ResultMsg  *CDATA `xml:"result_msg,omitempty" json:"result_msg,omitempty"`
}

// H5ContractReq H5纯签约参数
type H5ContractReq struct {
	ContractBase
	// 商户请求签约时的序列号，要求唯一性。禁止使用0开头，序列号主要用于排序，不作为查询条件，纯数字，范围不能超过int64的范围
	RequestSerial int64
	// 签约用户的名称，用于页面展示，参数值不支持UTF8非3字节编码的字符，例如表情符号，所以请勿传微信昵称到该字段
	ContractDisplayAccount string
	// 用于接收签约成功消息的回调通知地址
	NotifyUrl string
	// 固定值1.0
	Version string
	// 系统当前时间，10位
	// 示例值：1414488825
	Timestamp string
	// MD5或者HMAC-SHA256，不填默认HMAC-SHA256
	SignType SignType
	// 用户IP地址
	ClientIp string
}

type H5ContractResp struct {
	ContractRespBase
}

// WebContractReq 公众号纯签约参数
type WebContractReq struct {
	ContractBase
	// 商户请求签约时的序列号，要求唯一性。禁止使用0开头，序列号主要用于排序，不作为查询条件，纯数字，范围不能超过int64的范围
	RequestSerial int64
	// 签约用户的名称，用于页面展示，参数值不支持UTF8非3字节编码的字符，例如表情符号，所以请勿传微信昵称到该字段
	ContractDisplayAccount string
	// 用于接收签约成功消息的回调通知地址
	NotifyUrl string
	// 固定值1.0
	Version string
	// 系统当前时间，10位
	// 示例值：1414488825
	Timestamp string
}

type WebContractResp struct {
	ContractRespBase
}

// *******************************************************************************
// 公众号纯签约
// *******************************************************************************

// WebContract 公众号纯签约
// ctx 上下文
// param 公众号纯签约参数
func WebContract(ctx context.Context,
	param *WebContractReq) (resp *WebContractResp,
	err error) {
	paramMap, err := validInputParma(param)
	if err != nil {
		return nil, err
	}
	req := nets.NewWebHttpReq()
	response, err := req.
		SetHeader("Content-Type", "application/xml").
		SetMethod(http.MethodGet).
		SetUrl(EntrustWeb.GetFullApiUrl(China)).
		SetUrlWithParams(paramMap).
		SetContext(ctx).
		Send()
	if err != nil {
		return nil, err
	}
	xmlStr := req.ParseResponseToString(response)
	err = xml.Unmarshal([]byte(xmlStr), &resp)
	if err != nil {
		return nil, err
	}
	return
}

// *******************************************************************************
// H5纯签约
// ******************************************************************************

// H5Contract H5纯签约
func H5Contract(ctx context.Context,
	param *H5ContractReq) (resp *H5ContractResp,
	err error) {
	paramMap, err := validInputH5(param)
	if err != nil {
		return nil, err
	}
	req := nets.NewWebHttpReq()
	response, err := req.
		SetHeader("Content-Type", "application/xml").
		SetMethod(http.MethodGet).
		SetUrl(EntrustH5.GetFullApiUrl(China)).
		SetUrlWithParams(paramMap).
		SetContext(ctx).
		Send()
	if err != nil {
		return nil, err
	}
	xmlStr := req.ParseResponseToString(response)
	err = xml.Unmarshal([]byte(xmlStr), &resp)
	if err != nil {
		return nil, err
	}
	return
}

// *******************************************************************************
// 私有方法
// *******************************************************************************

// 获取参数与校检
func validInputParma(param *WebContractReq) (
	paramMap map[string]any,
	err error) {
	if param == nil {
		return nil, errors.New("param is nil")
	}
	if !validator.IsEmptyString(param.AppId) {
		return nil, errors.New("param `appId` is empty")
	}
	if !validator.IsEmptyString(param.MchId) {
		return nil, errors.New("param `mchId` is empty")
	}
	if !validator.IsEmptyString(param.PlanId) {
		return nil, errors.New("param `planId` is empty")
	}
	if !validator.IsEmptyString(param.ContractCode) {
		return nil, errors.New("param `contractCode` is empty")
	}
	if !validator.IsEmptyString(param.ContractDisplayAccount) {
		return nil, errors.New("param `contractDisplayAccount` is empty")
	}
	if !validator.IsEmptyString(param.NotifyUrl) {
		return nil, errors.New("param `notifyUrl` is empty")
	}
	if !validator.IsEmptyString(param.Version) {
		return nil, errors.New("param `version` is empty")
	}
	if !validator.IsEmptyString(param.Timestamp) {
		return nil, errors.New("param `timestamp` is empty")
	}
	if param.RequestSerial <= 0 {
		return nil, errors.New("param `requestSerial` is empty")
	}
	signMap := make(map[string]any)
	signMap["appid"] = param.AppId
	signMap["mch_id"] = param.MchId
	signMap["plan_id"] = param.PlanId
	signMap["contract_code"] = param.ContractCode
	signMap["contract_display_account"] = param.ContractDisplayAccount
	signMap["notify_url"] = param.NotifyUrl
	signMap["version"] = param.Version
	signMap["request_serial"] = param.RequestSerial
	signMap["timestamp"] = param.Timestamp
	sortStr := SortedMapToString(signMap, param.AppId)
	signMap["sign"] = CreateWeChatSign(SignTypeMd5, sortStr, param.AppId)
	return signMap, nil
}

// 获取H5签约参数
func validInputH5(param *H5ContractReq) (paramMap map[string]any,
	err error) {
	if param == nil {
		return nil, errors.New("param is nil")
	}
	if !validator.IsEmptyString(param.AppId) {
		return nil, errors.New("param `appId` is empty")
	}
	if !validator.IsEmptyString(param.MchId) {
		return nil, errors.New("param `mchId` is empty")
	}
	if !validator.IsEmptyString(param.PlanId) {
		return nil, errors.New("param `planId` is empty")
	}
	if !validator.IsEmptyString(param.ContractCode) {
		return nil, errors.New("param `contractCode` is empty")
	}
	if !validator.IsEmptyString(param.ContractDisplayAccount) {
		return nil, errors.New("param `contractDisplayAccount` is empty")
	}
	if !validator.IsEmptyString(param.NotifyUrl) {
		return nil, errors.New("param `notifyUrl` is empty")
	}
	if !validator.IsEmptyString(param.Version) {
		return nil, errors.New("param `version` is empty")
	}
	if !validator.IsEmptyString(param.Timestamp) {
		return nil, errors.New("param `timestamp` is empty")
	}
	if param.RequestSerial <= 0 {
		return nil, errors.New("param `requestSerial` is empty")
	}
	signMap := make(map[string]any)
	signMap["appid"] = param.AppId
	signMap["mch_id"] = param.MchId
	signMap["plan_id"] = param.PlanId
	signMap["contract_code"] = param.ContractCode
	signMap["contract_display_account"] = param.ContractDisplayAccount
	signMap["notify_url"] = param.NotifyUrl
	signMap["version"] = param.Version
	signMap["request_serial"] = param.RequestSerial
	signMap["timestamp"] = param.Timestamp
	signMap["client_ip"] = param.ClientIp
	sortStr := SortedMapToString(signMap, param.AppId)
	signMap["sign"] = CreateWeChatSign(SignTypeHMACSHA256, sortStr, param.AppId)
	return signMap, nil
}
